{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e4667eea-4b28-4351-81d6-c2239da0c052",
   "metadata": {},
   "source": [
    "# 实验8 基于全连接深度神经网络模型的Wi-Fi动作感知"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00a97513-1029-4c68-a330-a4f6d0ec549d",
   "metadata": {},
   "source": [
    "#### 数据读取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "63d78103-0b34-4824-aceb-49c901d2f9d3",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "D:/BaiduNetdiskDownload/U1_G1_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 1]\n",
      "D:/BaiduNetdiskDownload/U1_G2_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 2]\n",
      "D:/BaiduNetdiskDownload/U1_G3_N30_L_L1_D0_20200408_2_Labeled.csv\n",
      "[0 3]\n",
      "D:/BaiduNetdiskDownload/U1_G1_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 1]\n",
      "D:/BaiduNetdiskDownload/U1_G2_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 2]\n",
      "D:/BaiduNetdiskDownload/U1_G3_N10_L_L1_D0_20200408_1_Labeled.csv\n",
      "[0 3]\n",
      "Training segments: 183\n",
      "Training Segment 1: 6414\n",
      "Training Segment 2: 3504\n",
      "Training Segment 3: 1214\n",
      "Training Segment 4: 2960\n",
      "Training Segment 5: 974\n",
      "Training Segment 6: 3024\n",
      "Training Segment 7: 814\n",
      "Training Segment 8: 3248\n",
      "Training Segment 9: 734\n",
      "Training Segment 10: 2928\n",
      "Training Segment 11: 782\n",
      "Training Segment 12: 2480\n",
      "Training Segment 13: 1102\n",
      "Training Segment 14: 2656\n",
      "Training Segment 15: 750\n",
      "Training Segment 16: 2672\n",
      "Training Segment 17: 606\n",
      "Training Segment 18: 3200\n",
      "Training Segment 19: 350\n",
      "Training Segment 20: 3392\n",
      "Training Segment 21: 878\n",
      "Training Segment 22: 3136\n",
      "Training Segment 23: 686\n",
      "Training Segment 24: 3408\n",
      "Training Segment 25: 558\n",
      "Training Segment 26: 3056\n",
      "Training Segment 27: 798\n",
      "Training Segment 28: 3472\n",
      "Training Segment 29: 670\n",
      "Training Segment 30: 3328\n",
      "Training Segment 31: 446\n",
      "Training Segment 32: 3472\n",
      "Training Segment 33: 798\n",
      "Training Segment 34: 2960\n",
      "Training Segment 35: 1438\n",
      "Training Segment 36: 3184\n",
      "Training Segment 37: 1134\n",
      "Training Segment 38: 2736\n",
      "Training Segment 39: 718\n",
      "Training Segment 40: 3200\n",
      "Training Segment 41: 942\n",
      "Training Segment 42: 3168\n",
      "Training Segment 43: 1454\n",
      "Training Segment 44: 2848\n",
      "Training Segment 45: 1342\n",
      "Training Segment 46: 3168\n",
      "Training Segment 47: 910\n",
      "Training Segment 48: 2624\n",
      "Training Segment 49: 1198\n",
      "Training Segment 50: 3024\n",
      "Training Segment 51: 974\n",
      "Training Segment 52: 3088\n",
      "Training Segment 53: 974\n",
      "Training Segment 54: 3200\n",
      "Training Segment 55: 558\n",
      "Training Segment 56: 3408\n",
      "Training Segment 57: 798\n",
      "Training Segment 58: 2928\n",
      "Training Segment 59: 1038\n",
      "Training Segment 60: 3424\n",
      "Training Segment 61: 7663\n",
      "Training Segment 62: 7870\n",
      "Training Segment 63: 3632\n",
      "Training Segment 64: 1502\n",
      "Training Segment 65: 4240\n",
      "Training Segment 66: 1102\n",
      "Training Segment 67: 3392\n",
      "Training Segment 68: 1582\n",
      "Training Segment 69: 4096\n",
      "Training Segment 70: 1822\n",
      "Training Segment 71: 3520\n",
      "Training Segment 72: 1470\n",
      "Training Segment 73: 3936\n",
      "Training Segment 74: 1118\n",
      "Training Segment 75: 4480\n",
      "Training Segment 76: 1246\n",
      "Training Segment 77: 3552\n",
      "Training Segment 78: 1646\n",
      "Training Segment 79: 3824\n",
      "Training Segment 80: 2094\n",
      "Training Segment 81: 3440\n",
      "Training Segment 82: 2270\n",
      "Training Segment 83: 4160\n",
      "Training Segment 84: 1630\n",
      "Training Segment 85: 4096\n",
      "Training Segment 86: 1630\n",
      "Training Segment 87: 4256\n",
      "Training Segment 88: 1774\n",
      "Training Segment 89: 3472\n",
      "Training Segment 90: 2270\n",
      "Training Segment 91: 3600\n",
      "Training Segment 92: 2110\n",
      "Training Segment 93: 3696\n",
      "Training Segment 94: 2190\n",
      "Training Segment 95: 3888\n",
      "Training Segment 96: 1902\n",
      "Training Segment 97: 4208\n",
      "Training Segment 98: 1886\n",
      "Training Segment 99: 4256\n",
      "Training Segment 100: 2030\n",
      "Training Segment 101: 4048\n",
      "Training Segment 102: 2478\n",
      "Training Segment 103: 4000\n",
      "Training Segment 104: 2238\n",
      "Training Segment 105: 4224\n",
      "Training Segment 106: 3022\n",
      "Training Segment 107: 4480\n",
      "Training Segment 108: 1310\n",
      "Training Segment 109: 4208\n",
      "Training Segment 110: 2174\n",
      "Training Segment 111: 4096\n",
      "Training Segment 112: 2238\n",
      "Training Segment 113: 4144\n",
      "Training Segment 114: 2766\n",
      "Training Segment 115: 4832\n",
      "Training Segment 116: 1454\n",
      "Training Segment 117: 4224\n",
      "Training Segment 118: 4526\n",
      "Training Segment 119: 4336\n",
      "Training Segment 120: 3438\n",
      "Training Segment 121: 4464\n",
      "Training Segment 122: 9807\n",
      "Training Segment 123: 17694\n",
      "Training Segment 124: 2512\n",
      "Training Segment 125: 1758\n",
      "Training Segment 126: 2192\n",
      "Training Segment 127: 1150\n",
      "Training Segment 128: 2592\n",
      "Training Segment 129: 1694\n",
      "Training Segment 130: 2176\n",
      "Training Segment 131: 1198\n",
      "Training Segment 132: 2144\n",
      "Training Segment 133: 1710\n",
      "Training Segment 134: 2032\n",
      "Training Segment 135: 2222\n",
      "Training Segment 136: 2256\n",
      "Training Segment 137: 2110\n",
      "Training Segment 138: 1760\n",
      "Training Segment 139: 2718\n",
      "Training Segment 140: 2176\n",
      "Training Segment 141: 2110\n",
      "Training Segment 142: 2112\n",
      "Training Segment 143: 2798\n",
      "Training Segment 144: 2400\n",
      "Training Segment 145: 2558\n",
      "Training Segment 146: 2208\n",
      "Training Segment 147: 2926\n",
      "Training Segment 148: 2496\n",
      "Training Segment 149: 2590\n",
      "Training Segment 150: 2448\n",
      "Training Segment 151: 2526\n",
      "Training Segment 152: 2416\n",
      "Training Segment 153: 3006\n",
      "Training Segment 154: 2320\n",
      "Training Segment 155: 3518\n",
      "Training Segment 156: 2032\n",
      "Training Segment 157: 5406\n",
      "Training Segment 158: 2240\n",
      "Training Segment 159: 5278\n",
      "Training Segment 160: 2304\n",
      "Training Segment 161: 8094\n",
      "Training Segment 162: 2192\n",
      "Training Segment 163: 22366\n",
      "Training Segment 164: 2352\n",
      "Training Segment 165: 1726\n",
      "Training Segment 166: 2768\n",
      "Training Segment 167: 2430\n",
      "Training Segment 168: 2176\n",
      "Training Segment 169: 3166\n",
      "Training Segment 170: 2208\n",
      "Training Segment 171: 3950\n",
      "Training Segment 172: 3360\n",
      "Training Segment 173: 3278\n",
      "Training Segment 174: 2672\n",
      "Training Segment 175: 2558\n",
      "Training Segment 176: 2400\n",
      "Training Segment 177: 3630\n",
      "Training Segment 178: 1712\n",
      "Training Segment 179: 3838\n",
      "Training Segment 180: 2240\n",
      "Training Segment 181: 2606\n",
      "Training Segment 182: 2416\n",
      "Training Segment 183: 8559\n",
      "Testing segments: 63\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "# Define training and testing files\n",
    "training_files = [\n",
    "    'D:/BaiduNetdiskDownload/U1_G1_N30_L_L1_D0_20200408_2_Labeled.csv',\n",
    "    'D:/BaiduNetdiskDownload/U1_G2_N30_L_L1_D0_20200408_2_Labeled.csv',\n",
    "    'D:/BaiduNetdiskDownload/U1_G3_N30_L_L1_D0_20200408_2_Labeled.csv']\n",
    "\n",
    "testing_files = [\n",
    "    'D:/BaiduNetdiskDownload/U1_G1_N10_L_L1_D0_20200408_1_Labeled.csv',\n",
    "    'D:/BaiduNetdiskDownload/U1_G2_N10_L_L1_D0_20200408_1_Labeled.csv',\n",
    "    'D:/BaiduNetdiskDownload/U1_G3_N10_L_L1_D0_20200408_1_Labeled.csv']\n",
    "\n",
    "# Function to read and process CSV files\n",
    "def read_csv_file(file_path):\n",
    "    print(file_path)\n",
    "    df = pd.read_csv(file_path)\n",
    "    csi = df.iloc[:, 2:].values  # All columns except 'timestamp' and 'label'\n",
    "    label = df['label'].values # 0: static, 1: up, 2: down, 3: left, 4: right\n",
    "    timestamp = df['timestamp'].values\n",
    "    print(np.unique(label))\n",
    "    return csi, label, timestamp\n",
    "\n",
    "def segment_signals(csi, label, timestamp):\n",
    "    segments = [] # Store segments\n",
    "    segment_label = label[0] # Initialize segment label\n",
    "    segment_start = 0 # Initialize segment start index\n",
    "\n",
    "    for i in range(len(label)): # Iterate through all labels\n",
    "        if label[i] != segment_label: # If the label is different from the current segment label\n",
    "            segments.append((csi[segment_start:i-1], segment_label, timestamp[segment_start:i-1])) # Append the current segment to the segments list\n",
    "            segment_start = i # Update the segment start index\n",
    "            segment_label = label[i] # Update the segment label\n",
    "\n",
    "    segments.append((csi[segment_start:], segment_label, timestamp[segment_start:])) # Append the last segment to the segments list\n",
    "    return segments\n",
    "\n",
    "# Define training and testing segments\n",
    "training_segments = []\n",
    "testing_segments = []\n",
    "\n",
    "# Read and process training files\n",
    "for file in training_files:\n",
    "    s, y, t = read_csv_file(file)\n",
    "    training_segments.extend(segment_signals(s, y, t))\n",
    "\n",
    "# Read and process testing files\n",
    "for file in testing_files:\n",
    "    s, y, t = read_csv_file(file)\n",
    "    testing_segments.extend(segment_signals(s, y, t))\n",
    "\n",
    "# Print sizes of the training segments and testing segments\n",
    "print(f\"Training segments: {len(training_segments)}\")\n",
    "\n",
    "# Print length of all training segments\n",
    "for i, (s, y, t) in enumerate(training_segments):\n",
    "    print(f\"Training Segment {i + 1}: {len(s)}\")\n",
    "\n",
    "# Print size of the testing segments\n",
    "print(f\"Testing segments: {len(testing_segments)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93d0b27a-0fcc-41bd-9074-71c122576d22",
   "metadata": {},
   "source": [
    "#### 数据对齐：转化为相同的形式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9fe0f944-35d7-4652-8c42-1a17a8a6283f",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from scipy.stats import kurtosis\n",
    "from scipy.stats import skew\n",
    "\n",
    "# Extract features of training segments\n",
    "def extract_features(s):\n",
    "    ''' \n",
    "    Extract features of each segment\n",
    "    features include:\n",
    "    - mean\n",
    "    - std\n",
    "    - max\n",
    "    - min\n",
    "    - median\n",
    "    - kurtosis\n",
    "    - skew\n",
    "    \n",
    "    Input:\n",
    "    s: segment (N*30) in training_segments or testing_segments\n",
    "    \n",
    "    Output:\n",
    "    x: 1-D vector (8*30)\n",
    "    '''\n",
    "    x = []\n",
    "    x.extend(np.mean(s, axis=0))\n",
    "    x.extend(np.std(s, axis=0))\n",
    "    x.extend(np.max(s, axis=0))\n",
    "    x.extend(np.min(s, axis=0))\n",
    "    x.extend(np.median(s, axis=0))\n",
    "    x.extend(kurtosis(s, axis=0))\n",
    "    x.extend(skew(s, axis=0))\n",
    "\n",
    "    return np.array(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb2eeadd-e7ab-47f8-afab-259776b41a9d",
   "metadata": {},
   "source": [
    "#### 使用extract_features创建训练集和测试集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "8aafa927-04d4-438f-b468-22ed4ca12165",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[1., 2., 3.],\n",
      "        [4., 5., 6.],\n",
      "        [7., 8., 9.]])\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\26463\\AppData\\Local\\Temp\\ipykernel_2744\\1453094627.py:27: UserWarning: Creating a tensor from a list of numpy.ndarrays is extremely slow. Please consider converting the list to a single numpy.ndarray with numpy.array() before converting to a tensor. (Triggered internally at C:\\actions-runner\\_work\\pytorch\\pytorch\\builder\\windows\\pytorch\\torch\\csrc\\utils\\tensor_new.cpp:281.)\n",
      "  trX = torch.tensor(trX, dtype=torch.float32) # Convert trX to tensor\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "from torch.utils.data import DataLoader, TensorDataset\n",
    "\n",
    "# 假设 trX 是一个包含多个numpy数组的列表\n",
    "trX = [np.array([1, 2, 3]), np.array([4, 5, 6]), np.array([7, 8, 9])]\n",
    "\n",
    "# 将列表转换为一个单一的numpy数组\n",
    "trX_np = np.array(trX, dtype=np.float32)  # 指定数据类型为float32\n",
    "\n",
    "# 将numpy数组转换为PyTorch张量\n",
    "trX = torch.tensor(trX_np, dtype=torch.float32)  # 转换为张量\n",
    "\n",
    "print(trX)\n",
    "\n",
    "def one_hot_collate(batch):\n",
    "    data = torch.stack([item[0] for item in batch])\n",
    "    labels = torch.tensor([item[1] for item in batch])\n",
    "    \n",
    "    one_hot_labels = torch.zeros(labels.size(0), 4)  # 4 classes\n",
    "    one_hot_labels.scatter_(1, labels.unsqueeze(1), 1)\n",
    "    return data, one_hot_labels\n",
    "\n",
    "batch_size = 4\n",
    "\n",
    "# Build training dataset\n",
    "trX = [extract_features(s) for s, _, _ in training_segments] # Extract features of training segments\n",
    "trX = torch.tensor(trX, dtype=torch.float32) # Convert trX to tensor\n",
    "trY = [y for _, y, _ in training_segments] # Extract labels of training segments\n",
    "trY = torch.tensor(trY) # Convert trY to tensor\n",
    "\n",
    "# Build testing dataset\n",
    "teX = [extract_features(s) for s, _, _ in testing_segments] # Extract features of testing segments\n",
    "teX = torch.tensor(teX, dtype=torch.float32) # Convert teX to tensor\n",
    "teY = [y for _, y, _ in testing_segments] # Extract labels of testing segments\n",
    "teY = torch.tensor(teY) # Convert teY to tensor\n",
    "\n",
    "# Normalize trX and teX\n",
    "# Calculate mean and standard deviation from the training data\n",
    "mean = trX.mean(dim=0)\n",
    "std = trX.std(dim=0)\n",
    "\n",
    "# Normalize training data\n",
    "trX = (trX - mean) / std\n",
    "\n",
    "# Normalize testing data using training mean and std\n",
    "teX = (teX - mean) / std\n",
    "\n",
    "# Build Dataset\n",
    "trDataset = TensorDataset(trX, trY) # Create training dataset\n",
    "teDataset = TensorDataset(teX, teY) # Create testing dataset\n",
    "\n",
    "# Build loader\n",
    "trLoader = DataLoader(trDataset, batch_size=batch_size, shuffle=True, num_workers=0, collate_fn=one_hot_collate) # Create training dataloader\n",
    "teLoader = DataLoader(teDataset, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=one_hot_collate) # Create testing dataloader"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef74b9cf-3db8-4716-8ac9-b456fb8fe66e",
   "metadata": {},
   "source": [
    "### 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "29f65c80-8569-46de-9b9c-01e4775bf1cc",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import torch.nn as nn\n",
    "\n",
    "class FNN(nn.Module):\n",
    "    def __init__(self, input_size, hidden_size, num_classes):\n",
    "        super(FNN, self).__init__()\n",
    "        self.fc1 = nn.Linear(input_size, hidden_size)\n",
    "        self.relu1 = nn.ReLU()\n",
    "        self.fc2 = nn.Linear(hidden_size, hidden_size)\n",
    "        self.relu2 = nn.ReLU()\n",
    "        self.fc3 = nn.Linear(hidden_size, num_classes)\n",
    "        self.softmax = nn.Softmax(dim=1)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        x = self.fc1(x)\n",
    "        x = self.relu1(x)\n",
    "        x = self.fc2(x)\n",
    "        x = self.relu2(x)\n",
    "        x = self.fc3(x)\n",
    "        out = self.softmax(x)\n",
    "        return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4786c446-d369-43de-9af2-77177bcbb3d7",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "FNN(\n",
      "  (fc1): Linear(in_features=210, out_features=10, bias=True)\n",
      "  (relu1): ReLU()\n",
      "  (fc2): Linear(in_features=10, out_features=10, bias=True)\n",
      "  (relu2): ReLU()\n",
      "  (fc3): Linear(in_features=10, out_features=4, bias=True)\n",
      "  (softmax): Softmax(dim=1)\n",
      ")\n",
      "Epoch [1/250], Train Loss: 1.3404, CV Loss: 1.2790\n",
      "Epoch [2/250], Train Loss: 1.2078, CV Loss: 1.1159\n",
      "Epoch [3/250], Train Loss: 1.0829, CV Loss: 1.0576\n",
      "Epoch [4/250], Train Loss: 1.0281, CV Loss: 1.0494\n",
      "Epoch [5/250], Train Loss: 0.9950, CV Loss: 1.0473\n",
      "Epoch [6/250], Train Loss: 0.9730, CV Loss: 1.0457\n",
      "Epoch [7/250], Train Loss: 0.9572, CV Loss: 1.0439\n",
      "Epoch [8/250], Train Loss: 0.9428, CV Loss: 1.0420\n",
      "Epoch [9/250], Train Loss: 0.9309, CV Loss: 1.0407\n",
      "Epoch [10/250], Train Loss: 0.9157, CV Loss: 1.0392\n",
      "Epoch [11/250], Train Loss: 0.9000, CV Loss: 1.0354\n",
      "Epoch [12/250], Train Loss: 0.8779, CV Loss: 1.0372\n",
      "Epoch [13/250], Train Loss: 0.8491, CV Loss: 1.0315\n",
      "Epoch [14/250], Train Loss: 0.8308, CV Loss: 1.0279\n",
      "Epoch [15/250], Train Loss: 0.8167, CV Loss: 1.0293\n",
      "Epoch [16/250], Train Loss: 0.8071, CV Loss: 1.0179\n",
      "Epoch [17/250], Train Loss: 0.7985, CV Loss: 1.0098\n",
      "Epoch [18/250], Train Loss: 0.7923, CV Loss: 0.9729\n",
      "Epoch [19/250], Train Loss: 0.7858, CV Loss: 0.9505\n",
      "Epoch [20/250], Train Loss: 0.7796, CV Loss: 0.9371\n",
      "Epoch [21/250], Train Loss: 0.7757, CV Loss: 0.9345\n",
      "Epoch [22/250], Train Loss: 0.7711, CV Loss: 0.9382\n",
      "Epoch [23/250], Train Loss: 0.7676, CV Loss: 0.9267\n",
      "Epoch [24/250], Train Loss: 0.7659, CV Loss: 0.9335\n",
      "Epoch [25/250], Train Loss: 0.7646, CV Loss: 0.9307\n",
      "Epoch [26/250], Train Loss: 0.7631, CV Loss: 0.9299\n",
      "Epoch [27/250], Train Loss: 0.7621, CV Loss: 0.9310\n",
      "Epoch [28/250], Train Loss: 0.7610, CV Loss: 0.9304\n",
      "Epoch [29/250], Train Loss: 0.7603, CV Loss: 0.9314\n",
      "Epoch [30/250], Train Loss: 0.7596, CV Loss: 0.9329\n",
      "Epoch [31/250], Train Loss: 0.7591, CV Loss: 0.9337\n",
      "Epoch [32/250], Train Loss: 0.7586, CV Loss: 0.9352\n",
      "Epoch [33/250], Train Loss: 0.7582, CV Loss: 0.9362\n",
      "Epoch [34/250], Train Loss: 0.7578, CV Loss: 0.9370\n",
      "Epoch [35/250], Train Loss: 0.7576, CV Loss: 0.9369\n",
      "Epoch [36/250], Train Loss: 0.7572, CV Loss: 0.9383\n",
      "Epoch [37/250], Train Loss: 0.7569, CV Loss: 0.9394\n",
      "Epoch [38/250], Train Loss: 0.7567, CV Loss: 0.9405\n",
      "Epoch [39/250], Train Loss: 0.7563, CV Loss: 0.9417\n",
      "Epoch [40/250], Train Loss: 0.7561, CV Loss: 0.9442\n",
      "Epoch [41/250], Train Loss: 0.7558, CV Loss: 0.9450\n",
      "Epoch [42/250], Train Loss: 0.7556, CV Loss: 0.9461\n",
      "Epoch [43/250], Train Loss: 0.7553, CV Loss: 0.9470\n",
      "Epoch [44/250], Train Loss: 0.7550, CV Loss: 0.9491\n",
      "Epoch [45/250], Train Loss: 0.7547, CV Loss: 0.9499\n",
      "Epoch [46/250], Train Loss: 0.7544, CV Loss: 0.9497\n",
      "Epoch [47/250], Train Loss: 0.7539, CV Loss: 0.9515\n",
      "Epoch [48/250], Train Loss: 0.7533, CV Loss: 0.9527\n",
      "Epoch [49/250], Train Loss: 0.7528, CV Loss: 0.9548\n",
      "Epoch [50/250], Train Loss: 0.7522, CV Loss: 0.9521\n",
      "Epoch [51/250], Train Loss: 0.7516, CV Loss: 0.9539\n",
      "Epoch [52/250], Train Loss: 0.7512, CV Loss: 0.9522\n",
      "Epoch [53/250], Train Loss: 0.7509, CV Loss: 0.9539\n",
      "Epoch [54/250], Train Loss: 0.7506, CV Loss: 0.9542\n",
      "Epoch [55/250], Train Loss: 0.7504, CV Loss: 0.9532\n",
      "Epoch [56/250], Train Loss: 0.7503, CV Loss: 0.9566\n",
      "Epoch [57/250], Train Loss: 0.7502, CV Loss: 0.9513\n",
      "Epoch [58/250], Train Loss: 0.7500, CV Loss: 0.9590\n",
      "Epoch [59/250], Train Loss: 0.7499, CV Loss: 0.9526\n",
      "Epoch [60/250], Train Loss: 0.7499, CV Loss: 0.9578\n",
      "Epoch [61/250], Train Loss: 0.7498, CV Loss: 0.9558\n",
      "Epoch [62/250], Train Loss: 0.7498, CV Loss: 0.9625\n",
      "Epoch [63/250], Train Loss: 0.7496, CV Loss: 0.9545\n",
      "Epoch [64/250], Train Loss: 0.7496, CV Loss: 0.9655\n",
      "Epoch [65/250], Train Loss: 0.7495, CV Loss: 0.9611\n",
      "Epoch [66/250], Train Loss: 0.7496, CV Loss: 0.9652\n",
      "Epoch [67/250], Train Loss: 0.7494, CV Loss: 0.9573\n",
      "Epoch [68/250], Train Loss: 0.7494, CV Loss: 0.9640\n",
      "Epoch [69/250], Train Loss: 0.7495, CV Loss: 0.9591\n",
      "Epoch [70/250], Train Loss: 0.7494, CV Loss: 0.9668\n",
      "Epoch [71/250], Train Loss: 0.7494, CV Loss: 0.9592\n",
      "Epoch [72/250], Train Loss: 0.7493, CV Loss: 0.9683\n",
      "Epoch [73/250], Train Loss: 0.7493, CV Loss: 0.9584\n",
      "Epoch [74/250], Train Loss: 0.7494, CV Loss: 0.9661\n",
      "Epoch [75/250], Train Loss: 0.7492, CV Loss: 0.9593\n",
      "Epoch [76/250], Train Loss: 0.7494, CV Loss: 0.9644\n",
      "Epoch [77/250], Train Loss: 0.7491, CV Loss: 0.9616\n",
      "Epoch [78/250], Train Loss: 0.7492, CV Loss: 0.9690\n",
      "Epoch [79/250], Train Loss: 0.7492, CV Loss: 0.9584\n",
      "Epoch [80/250], Train Loss: 0.7492, CV Loss: 0.9687\n",
      "Epoch [81/250], Train Loss: 0.7492, CV Loss: 0.9579\n",
      "Epoch [82/250], Train Loss: 0.7492, CV Loss: 0.9684\n",
      "Epoch [83/250], Train Loss: 0.7492, CV Loss: 0.9615\n",
      "Epoch [84/250], Train Loss: 0.7492, CV Loss: 0.9688\n",
      "Epoch [85/250], Train Loss: 0.7491, CV Loss: 0.9598\n",
      "Epoch [86/250], Train Loss: 0.7492, CV Loss: 0.9658\n",
      "Epoch [87/250], Train Loss: 0.7490, CV Loss: 0.9624\n",
      "Epoch [88/250], Train Loss: 0.7491, CV Loss: 0.9683\n",
      "Epoch [89/250], Train Loss: 0.7492, CV Loss: 0.9646\n",
      "Epoch [90/250], Train Loss: 0.7491, CV Loss: 0.9725\n",
      "Epoch [91/250], Train Loss: 0.7491, CV Loss: 0.9612\n",
      "Epoch [92/250], Train Loss: 0.7492, CV Loss: 0.9671\n",
      "Epoch [93/250], Train Loss: 0.7489, CV Loss: 0.9727\n",
      "Epoch [94/250], Train Loss: 0.7491, CV Loss: 0.9634\n",
      "Epoch [95/250], Train Loss: 0.7491, CV Loss: 0.9704\n",
      "Epoch [96/250], Train Loss: 0.7490, CV Loss: 0.9623\n",
      "Epoch [97/250], Train Loss: 0.7493, CV Loss: 0.9636\n",
      "Epoch [98/250], Train Loss: 0.7490, CV Loss: 0.9762\n",
      "Epoch [99/250], Train Loss: 0.7493, CV Loss: 0.9725\n",
      "Epoch [100/250], Train Loss: 0.7490, CV Loss: 0.9621\n",
      "Epoch [101/250], Train Loss: 0.7492, CV Loss: 0.9648\n",
      "Epoch [102/250], Train Loss: 0.7491, CV Loss: 0.9734\n",
      "Epoch [103/250], Train Loss: 0.7491, CV Loss: 0.9660\n",
      "Epoch [104/250], Train Loss: 0.7491, CV Loss: 0.9730\n",
      "Epoch [105/250], Train Loss: 0.7489, CV Loss: 0.9665\n",
      "Epoch [106/250], Train Loss: 0.7492, CV Loss: 0.9658\n",
      "Epoch [107/250], Train Loss: 0.7491, CV Loss: 0.9729\n",
      "Epoch [108/250], Train Loss: 0.7491, CV Loss: 0.9667\n",
      "Epoch [109/250], Train Loss: 0.7491, CV Loss: 0.9687\n",
      "Epoch [110/250], Train Loss: 0.7489, CV Loss: 0.9780\n",
      "Epoch [111/250], Train Loss: 0.7492, CV Loss: 0.9736\n",
      "Epoch [112/250], Train Loss: 0.7489, CV Loss: 0.9656\n",
      "Epoch [113/250], Train Loss: 0.7492, CV Loss: 0.9701\n",
      "Epoch [114/250], Train Loss: 0.7488, CV Loss: 0.9728\n",
      "Epoch [115/250], Train Loss: 0.7488, CV Loss: 0.9739\n",
      "Epoch [116/250], Train Loss: 0.7489, CV Loss: 0.9656\n",
      "Epoch [117/250], Train Loss: 0.7492, CV Loss: 0.9668\n",
      "Epoch [118/250], Train Loss: 0.7491, CV Loss: 0.9718\n",
      "Epoch [119/250], Train Loss: 0.7488, CV Loss: 0.9685\n",
      "Epoch [120/250], Train Loss: 0.7490, CV Loss: 0.9776\n",
      "Epoch [121/250], Train Loss: 0.7492, CV Loss: 0.9746\n",
      "Epoch [122/250], Train Loss: 0.7490, CV Loss: 0.9649\n",
      "Epoch [123/250], Train Loss: 0.7492, CV Loss: 0.9673\n",
      "Epoch [124/250], Train Loss: 0.7491, CV Loss: 0.9722\n",
      "Epoch [125/250], Train Loss: 0.7488, CV Loss: 0.9772\n",
      "Epoch [126/250], Train Loss: 0.7491, CV Loss: 0.9682\n",
      "Epoch [127/250], Train Loss: 0.7491, CV Loss: 0.9750\n",
      "Epoch [128/250], Train Loss: 0.7491, CV Loss: 0.9733\n",
      "Epoch [129/250], Train Loss: 0.7490, CV Loss: 0.9851\n",
      "Epoch [130/250], Train Loss: 0.7492, CV Loss: 0.9764\n",
      "Epoch [131/250], Train Loss: 0.7491, CV Loss: 0.9695\n",
      "Epoch [132/250], Train Loss: 0.7491, CV Loss: 0.9777\n",
      "Epoch [133/250], Train Loss: 0.7491, CV Loss: 0.9756\n",
      "Epoch [134/250], Train Loss: 0.7489, CV Loss: 0.9928\n",
      "Epoch [135/250], Train Loss: 0.7492, CV Loss: 0.9811\n",
      "Epoch [136/250], Train Loss: 0.7491, CV Loss: 0.9779\n",
      "Epoch [137/250], Train Loss: 0.7489, CV Loss: 0.9701\n",
      "Epoch [138/250], Train Loss: 0.7492, CV Loss: 0.9721\n",
      "Epoch [139/250], Train Loss: 0.7491, CV Loss: 0.9738\n",
      "Epoch [140/250], Train Loss: 0.7488, CV Loss: 0.9829\n",
      "Epoch [141/250], Train Loss: 0.7492, CV Loss: 0.9800\n",
      "Epoch [142/250], Train Loss: 0.7491, CV Loss: 0.9736\n",
      "Epoch [143/250], Train Loss: 0.7490, CV Loss: 0.9795\n",
      "Epoch [144/250], Train Loss: 0.7491, CV Loss: 0.9769\n",
      "Epoch [145/250], Train Loss: 0.7488, CV Loss: 0.9799\n",
      "Epoch [146/250], Train Loss: 0.7492, CV Loss: 0.9933\n",
      "Epoch [147/250], Train Loss: 0.7491, CV Loss: 0.9868\n",
      "Epoch [148/250], Train Loss: 0.7491, CV Loss: 0.9817\n",
      "Epoch [149/250], Train Loss: 0.7491, CV Loss: 0.9785\n",
      "Epoch [150/250], Train Loss: 0.7491, CV Loss: 0.9725\n",
      "Epoch [151/250], Train Loss: 0.7490, CV Loss: 0.9803\n",
      "Epoch [152/250], Train Loss: 0.7491, CV Loss: 0.9786\n",
      "Epoch [153/250], Train Loss: 0.7489, CV Loss: 0.9748\n",
      "Epoch [154/250], Train Loss: 0.7492, CV Loss: 0.9716\n",
      "Epoch [155/250], Train Loss: 0.7491, CV Loss: 0.9700\n",
      "Epoch [156/250], Train Loss: 0.7491, CV Loss: 0.9717\n",
      "Epoch [157/250], Train Loss: 0.7490, CV Loss: 0.9745\n",
      "Epoch [158/250], Train Loss: 0.7492, CV Loss: 0.9881\n",
      "Epoch [159/250], Train Loss: 0.7491, CV Loss: 0.9811\n",
      "Epoch [160/250], Train Loss: 0.7490, CV Loss: 0.9711\n",
      "Epoch [161/250], Train Loss: 0.7491, CV Loss: 0.9720\n",
      "Epoch [162/250], Train Loss: 0.7490, CV Loss: 0.9893\n",
      "Epoch [163/250], Train Loss: 0.7491, CV Loss: 0.9825\n",
      "Epoch [164/250], Train Loss: 0.7491, CV Loss: 0.9757\n",
      "Epoch [165/250], Train Loss: 0.7489, CV Loss: 0.9728\n",
      "Epoch [166/250], Train Loss: 0.7493, CV Loss: 0.9796\n",
      "Epoch [167/250], Train Loss: 0.7491, CV Loss: 0.9752\n",
      "Epoch [168/250], Train Loss: 0.7491, CV Loss: 0.9738\n",
      "Epoch [169/250], Train Loss: 0.7491, CV Loss: 0.9730\n",
      "Epoch [170/250], Train Loss: 0.7491, CV Loss: 0.9721\n",
      "Epoch [171/250], Train Loss: 0.7491, CV Loss: 0.9812\n",
      "Epoch [172/250], Train Loss: 0.7491, CV Loss: 0.9731\n",
      "Epoch [173/250], Train Loss: 0.7487, CV Loss: 0.9713\n",
      "Epoch [174/250], Train Loss: 0.7491, CV Loss: 0.9722\n",
      "Epoch [175/250], Train Loss: 0.7491, CV Loss: 0.9761\n",
      "Epoch [176/250], Train Loss: 0.7490, CV Loss: 0.9709\n",
      "Epoch [177/250], Train Loss: 0.7491, CV Loss: 0.9736\n",
      "Epoch [178/250], Train Loss: 0.7491, CV Loss: 0.9728\n",
      "Epoch [179/250], Train Loss: 0.7491, CV Loss: 0.9734\n",
      "Epoch [180/250], Train Loss: 0.7491, CV Loss: 0.9741\n",
      "Epoch [181/250], Train Loss: 0.7487, CV Loss: 0.9740\n",
      "Epoch [182/250], Train Loss: 0.7491, CV Loss: 0.9724\n",
      "Epoch [183/250], Train Loss: 0.7491, CV Loss: 0.9726\n",
      "Epoch [184/250], Train Loss: 0.7491, CV Loss: 0.9735\n",
      "Epoch [185/250], Train Loss: 0.7491, CV Loss: 0.9804\n",
      "Epoch [186/250], Train Loss: 0.7490, CV Loss: 0.9728\n",
      "Epoch [187/250], Train Loss: 0.7491, CV Loss: 0.9740\n",
      "Epoch [188/250], Train Loss: 0.7491, CV Loss: 0.9966\n",
      "Epoch [189/250], Train Loss: 0.7491, CV Loss: 0.9906\n",
      "Epoch [190/250], Train Loss: 0.7491, CV Loss: 0.9783\n",
      "Epoch [191/250], Train Loss: 0.7488, CV Loss: 0.9805\n",
      "Epoch [192/250], Train Loss: 0.7493, CV Loss: 0.9798\n",
      "Epoch [193/250], Train Loss: 0.7491, CV Loss: 0.9765\n",
      "Epoch [194/250], Train Loss: 0.7491, CV Loss: 0.9750\n",
      "Epoch [195/250], Train Loss: 0.7491, CV Loss: 0.9743\n",
      "Epoch [196/250], Train Loss: 0.7491, CV Loss: 0.9742\n",
      "Epoch [197/250], Train Loss: 0.7491, CV Loss: 0.9733\n",
      "Epoch [198/250], Train Loss: 0.7491, CV Loss: 0.9729\n",
      "Epoch [199/250], Train Loss: 0.7491, CV Loss: 0.9723\n",
      "Epoch [200/250], Train Loss: 0.7491, CV Loss: 0.9732\n",
      "Epoch [201/250], Train Loss: 0.7491, CV Loss: 0.9751\n",
      "Epoch [202/250], Train Loss: 0.7490, CV Loss: 0.9799\n",
      "Epoch [203/250], Train Loss: 0.7492, CV Loss: 0.9921\n",
      "Epoch [204/250], Train Loss: 0.7491, CV Loss: 0.9824\n",
      "Epoch [205/250], Train Loss: 0.7491, CV Loss: 0.9744\n",
      "Epoch [206/250], Train Loss: 0.7491, CV Loss: 0.9780\n",
      "Epoch [207/250], Train Loss: 0.7491, CV Loss: 0.9937\n",
      "Epoch [208/250], Train Loss: 0.7491, CV Loss: 1.0027\n",
      "Epoch [209/250], Train Loss: 0.7491, CV Loss: 0.9945\n",
      "Epoch [210/250], Train Loss: 0.7487, CV Loss: 1.0008\n",
      "Epoch [211/250], Train Loss: 0.7492, CV Loss: 0.9901\n",
      "Epoch [212/250], Train Loss: 0.7491, CV Loss: 0.9888\n",
      "Epoch [213/250], Train Loss: 0.7491, CV Loss: 0.9865\n",
      "Epoch [214/250], Train Loss: 0.7491, CV Loss: 0.9856\n",
      "Epoch [215/250], Train Loss: 0.7491, CV Loss: 0.9849\n",
      "Epoch [216/250], Train Loss: 0.7491, CV Loss: 0.9849\n",
      "Epoch [217/250], Train Loss: 0.7491, CV Loss: 0.9851\n",
      "Epoch [218/250], Train Loss: 0.7491, CV Loss: 0.9843\n",
      "Epoch [219/250], Train Loss: 0.7489, CV Loss: 0.9878\n",
      "Epoch [220/250], Train Loss: 0.7491, CV Loss: 0.9751\n",
      "Epoch [221/250], Train Loss: 0.7491, CV Loss: 0.9742\n",
      "Epoch [222/250], Train Loss: 0.7491, CV Loss: 0.9727\n",
      "Epoch [223/250], Train Loss: 0.7491, CV Loss: 0.9728\n",
      "Epoch [224/250], Train Loss: 0.7491, CV Loss: 0.9735\n",
      "Epoch [225/250], Train Loss: 0.7491, CV Loss: 0.9741\n",
      "Epoch [226/250], Train Loss: 0.7491, CV Loss: 0.9748\n",
      "Epoch [227/250], Train Loss: 0.7491, CV Loss: 0.9757\n",
      "Epoch [228/250], Train Loss: 0.7491, CV Loss: 0.9759\n",
      "Epoch [229/250], Train Loss: 0.7491, CV Loss: 0.9770\n",
      "Epoch [230/250], Train Loss: 0.7491, CV Loss: 0.9776\n",
      "Epoch [231/250], Train Loss: 0.7491, CV Loss: 0.9781\n",
      "Epoch [232/250], Train Loss: 0.7491, CV Loss: 0.9791\n",
      "Epoch [233/250], Train Loss: 0.7491, CV Loss: 0.9802\n",
      "Epoch [234/250], Train Loss: 0.7491, CV Loss: 0.9807\n",
      "Epoch [235/250], Train Loss: 0.7491, CV Loss: 0.9817\n",
      "Epoch [236/250], Train Loss: 0.7491, CV Loss: 0.9824\n",
      "Epoch [237/250], Train Loss: 0.7491, CV Loss: 0.9827\n",
      "Epoch [238/250], Train Loss: 0.7491, CV Loss: 0.9832\n",
      "Epoch [239/250], Train Loss: 0.7491, CV Loss: 0.9838\n",
      "Epoch [240/250], Train Loss: 0.7491, CV Loss: 0.9844\n",
      "Epoch [241/250], Train Loss: 0.7491, CV Loss: 0.9850\n",
      "Epoch [242/250], Train Loss: 0.7491, CV Loss: 0.9853\n",
      "Epoch [243/250], Train Loss: 0.7491, CV Loss: 0.9857\n",
      "Epoch [244/250], Train Loss: 0.7491, CV Loss: 0.9860\n",
      "Epoch [245/250], Train Loss: 0.7491, CV Loss: 0.9865\n",
      "Epoch [246/250], Train Loss: 0.7491, CV Loss: 0.9865\n",
      "Epoch [247/250], Train Loss: 0.7491, CV Loss: 0.9870\n",
      "Epoch [248/250], Train Loss: 0.7491, CV Loss: 0.9870\n",
      "Epoch [249/250], Train Loss: 0.7491, CV Loss: 1.0059\n",
      "Epoch [250/250], Train Loss: 0.7490, CV Loss: 0.9847\n"
     ]
    }
   ],
   "source": [
    "# Define the model parameters\n",
    "hidden_size = 10\n",
    "\n",
    "# Instantiate the model\n",
    "input_size = trX.shape[1]\n",
    "num_classes = 4 # 3 movements and static\n",
    "model = FNN(input_size, hidden_size, num_classes)\n",
    "print(model)\n",
    "\n",
    "# Define loss function and optimizer\n",
    "criterion = nn.CrossEntropyLoss()\n",
    "optimizer = torch.optim.Adam(model.parameters())\n",
    "\n",
    "# Lists to store losses\n",
    "train_losses = []\n",
    "te_losses = []\n",
    "\n",
    "# Number of epochs\n",
    "num_epochs = 250\n",
    "\n",
    "for epoch in range(num_epochs):\n",
    "    model.train()\n",
    "    batch_losses = []\n",
    "    \n",
    "    for batch_x, batch_y in trLoader:\n",
    "        # Forward pass\n",
    "        outputs = model(batch_x)\n",
    "        loss = criterion(outputs, batch_y)\n",
    "        \n",
    "        # Backward pass and optimize\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        batch_losses.append(loss.item())\n",
    "    \n",
    "    # Calculate average training loss for this epoch\n",
    "    avg_train_loss = sum(batch_losses) / len(batch_losses)\n",
    "    train_losses.append(avg_train_loss)\n",
    "    \n",
    "    # Evaluate on cross-validation set\n",
    "    model.eval()\n",
    "    te_batch_losses = []\n",
    "    with torch.no_grad():\n",
    "        for te_x, te_y in teLoader:\n",
    "            te_outputs = model(te_x)\n",
    "            te_loss = criterion(te_outputs, te_y)\n",
    "            te_batch_losses.append(te_loss.item())\n",
    "    \n",
    "    avg_te_loss = sum(te_batch_losses) / len(te_batch_losses)\n",
    "    te_losses.append(avg_te_loss)\n",
    "    \n",
    "    print(f'Epoch [{epoch+1}/{num_epochs}], Train Loss: {avg_train_loss:.4f}, CV Loss: {avg_te_loss:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13c969aa-85c5-417b-846d-998e01372723",
   "metadata": {},
   "source": [
    "### 计算识别精度，展示学习曲线"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "f27245ce-1713-4d02-bccb-d7a3fe9f2d0d",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on training set: 99.45%\n",
      "Accuracy on cross-validation set: 74.60%\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA04AAAHACAYAAACVhTgAAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABzrklEQVR4nO3dd3xUVf7G8WdKJo0UQgsloSu9CKJgQ0WKgg17A8sqirqIusr6s+uirnXXFbvYRRQ7KqDSRKUIgoKAtAQIHdLrzP39cWYmCQRSCLmT5PN+vcbM3Lkzcya5jPeZc873OCzLsgQAAAAAOCin3Q0AAAAAgFBHcAIAAACAchCcAAAAAKAcBCcAAAAAKAfBCQAAAADKQXACAAAAgHIQnAAAAACgHAQnAAAAACiH2+4G1DSfz6etW7cqJiZGDofD7uYAAAAAsIllWcrMzFSLFi3kdB66T6neBaetW7cqKSnJ7mYAAAAACBGpqalq1arVIfepd8EpJiZGkvnlxMbG2twaAAAAAHbJyMhQUlJSMCMcSr0LToHhebGxsQQnAAAAABWawkNxCAAAAAAoB8EJAAAAAMpBcAIAAACActS7OU4AAACoO7xerwoLC+1uBkJYWFiYXC7XYT8PwQkAAAC1UlZWljZv3izLsuxuCkKYw+FQq1at1KBBg8N6HoITAAAAah2v16vNmzcrKipKTZo0qVBVNNQ/lmVp586d2rx5szp27HhYPU8EJwAAANQ6hYWFsixLTZo0UWRkpN3NQQhr0qSJNm7cqMLCwsMKThSHAAAAQK1FTxPKU13HCMEJAAAAAMpBcAIAAABqsYEDB2rcuHEV3n/jxo1yOBxatmzZEWtTXURwAgAAAGqAw+E45GX06NFVet5p06bp4YcfrvD+SUlJSktLU7du3ar0ehVV1wIaxSEAAACAGpCWlha8PmXKFN13331avXp1cNv+RS4KCwsVFhZW7vMmJCRUqh0ul0uJiYmVegzocQIAAABqRGJiYvASFxcnh8MRvJ2Xl6f4+Hh9+OGHGjhwoCIiIvTOO+9o9+7duvTSS9WqVStFRUWpe/fuev/990s97/5D9dq0aaN//etfuuaaaxQTE6Pk5GS9/PLLwfv37wmaPXu2HA6HvvvuO/Xt21dRUVEaMGBAqVAnSY888oiaNm2qmJgYXXfddbr77rvVq1evKv8+8vPzdeutt6pp06aKiIjQiSeeqEWLFgXv37t3ry6//PJg5cSOHTvqjTfekCQVFBTo5ptvVvPmzRUREaE2bdpo4sSJVW5LRRCcbPTZsi0a+uxcPfrVSrubAgAAUKtZlqWcgiJbLtW5AO9dd92lW2+9VatWrdKQIUOUl5enPn366Msvv9Tvv/+u66+/XldeeaV++eWXQz7PU089pb59+2rp0qW66aabdOONN+rPP/885GPuuecePfXUU1q8eLHcbreuueaa4H3vvvuuHn30UT3++ONasmSJkpOTNWnSpMN6r//4xz/08ccf680339Svv/6qDh06aMiQIdqzZ48k6d5779XKlSv19ddfa9WqVZo0aZIaN24sSfrPf/6jzz//XB9++KFWr16td955R23atDms9pSHoXo2yswr0p/bMtW6UZTdTQEAAKjVcgu96nLft7a89sqHhijKUz2n1ePGjdP5559fatsdd9wRvH7LLbfom2++0dSpU3Xccccd9HnOPPNM3XTTTZJMGHvmmWc0e/ZsderU6aCPefTRR3XKKadIku6++26dddZZysvLU0REhP773//q2muv1dVXXy1Juu+++zRjxgxlZWVV6X1mZ2dr0qRJmjx5soYNGyZJeuWVVzRz5ky99tpruvPOO5WSkqLevXurb9++klQqGKWkpKhjx4468cQT5XA41Lp16yq1ozLocbJRRJhZgCu30GdzSwAAABAKAiEhwOv16tFHH1WPHj3UqFEjNWjQQDNmzFBKSsohn6dHjx7B64EhgTt27KjwY5o3by5JwcesXr1a/fr1K7X//rcrY926dSosLNQJJ5wQ3BYWFqZ+/fpp1apVkqQbb7xRH3zwgXr16qV//OMfWrBgQXDf0aNHa9myZTr66KN16623asaMGVVuS0XR42SjiDCTW/MKvTa3BAAAoHaLDHNp5UNDbHvt6hIdHV3q9lNPPaVnnnlGzz77rLp3767o6GiNGzdOBQUFh3ye/YtKOBwO+XyH/rK+5GMCi8aWfMz+C8kezhDFwGPLes7AtmHDhmnTpk366quvNGvWLJ1++ukaO3asnnzySR1zzDHasGGDvv76a82aNUsXXXSRBg0apI8++qjKbSoPPU42Cvwjyyc4AQAAHBaHw6Eoj9uWy/4n/9Vp3rx5Ouecc3TFFVeoZ8+eateundauXXvEXu9gjj76aC1cuLDUtsWL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1BqjR4+Ww+E44DJ06FC7mwYAqOPcdjcAAIDKGDp0qN54441S28LDw21qDQCgvqDHCQBQq4SHhysxMbHUpWHDhpLMMLpJkyZp2LBhioyMVNu2bTV16tRSj1+xYoVOO+00RUZGqlGjRrr++uuVlZVVap/XX39dXbt2VXh4uJo3b66bb7651P27du3Seeedp6ioKHXs2FGff/75kX3TAADbEZwAAHXKvffeq5EjR+q3337TFVdcoUsvvVSrVq2SJOXk5Gjo0KFq2LChFi1apKlTp2rWrFmlgtGkSZM0duxYXX/99VqxYoU+//xzdejQodRrPPjgg7rooou0fPlynXnmmbr88su1Z8+eGn2fAICa5bAsy7K7EQAAVMTo0aP1zjvvKCIiotT2u+66S/fee68cDofGjBmjSZMmBe87/vjjdcwxx+iFF17QK6+8orvuukupqamKjo6WJE2fPl0jRozQ1q1b1axZM7Vs2VJXX321HnnkkTLb4HA49H//9396+OGHJUnZ2dmKiYnR9OnTmWsFAHUYc5wAALXKqaeeWioYSVJCQkLwev/+/Uvd179/fy1btkyStGrVKvXs2TMYmiTphBNOkM/n0+rVq+VwOLR161adfvrph2xDjx49gtejo6MVExOjHTt2VPUtAQBqAYITAKBWiY6OPmDoXHkcDockybKs4PWy9omMjKzQ84WFhR3wWJ/PV6k2AQBqF+Y4AQDqlJ9//vmA2506dZIkdenSRcuWLVN2dnbw/h9//FFOp1NHHXWUYmJi1KZNG3333Xc12mYAQOijxwkAUKvk5+dr27Ztpba53W41btxYkjR16lT17dtXJ554ot59910tXLhQr732miTp8ssv1/33369Ro0bpgQce0M6dO3XLLbfoyiuvVLNmzSRJDzzwgMaMGaOmTZtq2LBhyszM1I8//qhbbrmlZt8oACCkEJwAALXKN998o+bNm5fadvTRR+vPP/+UZCreffDBB7rpppuUmJiod999V126dJEkRUVF6dtvv9Xf//53HXvssYqKitLIkSP19NNPB59r1KhRysvL0zPPPKM77rhDjRs31gUXXFBzbxAAEJKoqgcAqDMcDoc++eQTnXvuuXY3BQBQxzDHCQAAAADKQXACAAAAgHIwxwkAUGcw+hwAcKTQ4wQAAAAA5SA4AQAAAEA5CE4AAAAAUA6CEwAAAACUg+AEAAAAAOUgOAEAAABAOQhOAAAAAFAOghMAAAAAlIPgBAAAAADl+H+xwKdaWhn6ywAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import os\n",
    "os.environ['KMP_DUPLICATE_LIB_OK']='True'\n",
    "\n",
    "# Calculate and print accuracies for training and cross-validation sets\n",
    "model.eval()\n",
    "with torch.no_grad():\n",
    "    # Training set accuracy\n",
    "    tr_correct = 0\n",
    "    tr_total = 0\n",
    "    for images, labels in trLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        tr_total += labels.size(0)\n",
    "        tr_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    tr_accuracy = 100 * tr_correct / tr_total\n",
    "    \n",
    "    # test set accuracy\n",
    "    te_correct = 0\n",
    "    te_total = 0\n",
    "    for images, labels in teLoader:\n",
    "        outputs = model(images)\n",
    "        _, predicted = torch.max(outputs, 1)\n",
    "        _, true_labels = torch.max(labels, 1)\n",
    "        te_total += labels.size(0)\n",
    "        te_correct += (predicted == true_labels).sum().item()\n",
    "    \n",
    "    te_accuracy = 100 * te_correct / te_total\n",
    "\n",
    "print(f'Accuracy on training set: {tr_accuracy:.2f}%')\n",
    "print(f'Accuracy on cross-validation set: {te_accuracy:.2f}%')\n",
    "\n",
    "# Plot training and cross-validation losses\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.plot(range(1, num_epochs+1), train_losses, label='Training Loss')\n",
    "plt.plot(range(1, num_epochs+1), te_losses, label='Testing Loss')\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.legend()\n",
    "plt.savefig('1.png', dpi=300) # Make figure clearer\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13770dda-23ee-4d58-9cb2-4bd9ff8c7cca",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
